package com.jacobson.core.widget.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.jacobson.core.R
import com.jacobson.core.framework.delegation.WeakRef
import com.jacobson.core.utils.WrapperUtil
import com.jacobson.core.widget.logger.LoggerFactory
import kotlinx.android.synthetic.main.item_content.view.*
import java.util.concurrent.atomic.AtomicInteger


/**
 * ================================================
 *
 *  @author Wucz on 2018/7/24 9:28
 * ================================================
 */

abstract class ListRecyclerAdapter<T>(
    context: Context
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    protected val logger =
        LoggerFactory.getLogger(this.javaClass, "${this.javaClass.simpleName}-${toString()} >>> ")
    protected val context by WeakRef { context }
    private val items: MutableList<T> = ArrayList(50)
    private val nextPage: AtomicInteger = AtomicInteger(1)
    private var isLoading: Boolean = false
    private var isErrorMode: Boolean = false
    private var errorInfo: ErrorInfo = ErrorInfo(-1, context.getString(R.string.txt_unknown_err))
    private var debug = false
    /**
     * 是否拥有更多数据
     * @param count 总数
     */
    fun hasMoreData(count: Int): Boolean {
        return items.size < count
    }

    /**
     * 下一分页
     */
    fun nextPage() = nextPage.get()

    /**
     * 分页列表
     */
    fun page(page: Int, dataList: List<T>) {
        if (debug) {
            logger.d("page: page = $page, data = $dataList")
        }
        if (page == nextPage.get()) {
            isLoading = false
            isErrorMode = false
            this.items.addAll(dataList)
            nextPage.incrementAndGet()
            notifyDataSetChanged()
        }
    }

    /** 追加 */
    fun append(dataList: List<T>) {
        if (dataList.isNotEmpty()) {
            isLoading = false
            isErrorMode = false
            this.items.addAll(dataList)
            notifyDataSetChanged()
        }
    }

    /**
     * 更新列表
     */
    fun update(dataList: List<T>?) {
        if (debug) {
            logger.d("update: $dataList")
        }
        this.items.clear()
        nextPage.set(2)
        if (dataList != null && dataList.isNotEmpty()) {
            isLoading = false
            isErrorMode = false
            this.items.addAll(dataList)
        }
        notifyDataSetChanged()
    }


    fun update(filter: (t: T) -> Boolean, doUpdate: (t: T) -> Unit) {
        val indexRange = arrayOf(Integer.MAX_VALUE, Integer.MIN_VALUE)
        items.filterIndexed { index, t ->
            if (filter(t)) {
                indexRange[0] = index.coerceAtMost(indexRange[0])
                indexRange[1] = index.coerceAtLeast(indexRange[1])
                true
            } else {
                false
            }
        }.forEach { doUpdate(it) }
        if (indexRange[0] == indexRange[1]) {
            notifyItemChanged(mappingPosition(indexRange[0], true))
        } else if (indexRange[0] != Integer.MAX_VALUE) {
            notifyItemRangeChanged(
                mappingPosition(indexRange[0], true),
                mappingPosition(indexRange[1], true)
            )
        }
    }

    fun remove(position: Int) {
        val index = mappingPosition(position, false)
        if (index in 0 until items.size) {
            items.removeAt(index)
            notifyItemRemoved(position)
            if (index != itemCount) {
                notifyItemRangeChanged(position, itemCount - position)
            } else {
                notifyDataSetChanged()
            }
        }
    }

    /**
     * 添加多个
     */
    fun addAll(dataList: List<T>, index: Int = -1) {
        val position = mappingPosition(index)
        if (position < 0) {
            this.items.addAll(dataList)
        } else {
            this.items.addAll(position, dataList)
        }
        notifyDataSetChanged()
    }

    fun clear() {
        update(null)
    }

    fun onDestroy() {
        this.items.clear()
    }

    fun getItems(): List<T> = items

    abstract fun onCreateContentViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder<T>

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (debug) {
            logger.i("${javaClass.simpleName}->onCreateViewHolder: $viewType")
        }
        return when (viewType) {
            VIEW_TYPE_EMPTY -> {
                onCreateEmptyViewHolder(parent)
            }
            VIEW_TYPE_LOADING -> {
                onCreateLoadingViewHolder(parent)
            }
            VIEW_TYPE_ERROR -> {
                onCreateErrorViewHolder(parent)
            }
            else -> {
                if (isExpansionViewType(viewType)) {
                    onCreateExpansionViewHolder(parent, viewType)
                } else {
                    onCreateContentViewHolder(parent, viewType)
                }
            }
        }
    }

    override fun getItemCount(): Int {
        if (isLoading || isEmpty() || isErrorMode) {
            return abnormalItemCount()
        }
        return getCustomItemCount()
    }

    override fun getItemViewType(position: Int): Int {
        val filter = filterItemViewType(position)
        return when {
            filter != VIEW_TYPE_FILTER -> filter
            isLoading -> VIEW_TYPE_LOADING
            isErrorMode -> VIEW_TYPE_ERROR
            isEmpty() -> VIEW_TYPE_EMPTY
            else -> getCustomViewType(position)
        }.apply {
            if (debug) {
                logger.i("${javaClass.simpleName}->getItemViewType: $this")
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (debug) {
            logger.i("${javaClass.simpleName}->onBindViewHolder: holder = $holder, position = $position")
        }
        when (holder) {
            is ViewHolder -> {
                holder.onBindView(items, position)
            }
            is ContentViewHolder<*> -> {
                @Suppress("UNCHECKED_CAST")
                (holder as ContentViewHolder<T>).let {
                    it.itemView.setOnClickListener { _ ->
                        onItemClickListener?.onItemClick(
                            items,
                            items[mappingPosition(position)],
                            position
                        )
                    }
                    it.onBindView(items, items[mappingPosition(position)], position)
                }
            }
            else -> {
                onBindUnknownViewHolder(items, holder, position)
            }
        }
    }

    override fun onBindViewHolder(
        holder: RecyclerView.ViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads)
            return
        }
        when (holder) {
            is ViewHolder -> {
                holder.onBindView(items, position)
            }
            is ContentViewHolder<*> -> {
                @Suppress("UNCHECKED_CAST")
                (holder as ContentViewHolder<T>).let {
                    it.itemView.setOnClickListener {
                        onItemClickListener?.onItemClick(
                            items,
                            items[mappingPosition(position)],
                            position
                        )
                    }
                    it.onBindView(items, items[mappingPosition(position)], position, payloads)
                }
            }
            else -> {
                onBindUnknownViewHolder(items, holder, position, payloads)
            }
        }
    }

    open fun onBindUnknownViewHolder(
        items: List<T>,
        holder: RecyclerView.ViewHolder,
        position: Int
    ) {
        throw UnknownViewHolderException()
    }

    open fun onBindUnknownViewHolder(
        items: List<T>,
        holder: RecyclerView.ViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        throw UnknownViewHolderException()
    }

    open fun onCreateExpansionViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return object : ViewHolder(context, parent, R.layout.item_content) {
            override fun onBindView(items: List<*>, position: Int) {
                itemView.itemContentInfo.text = context?.getString(R.string.txt_expansion)
            }
        }
    }

    open fun onCreateErrorViewHolder(parent: ViewGroup): ViewHolder {
        return object : ViewHolder(context, parent, R.layout.item_content) {
            override fun onBindView(items: List<*>, position: Int) {
                itemView.itemContentInfo.text =
                    context?.getString(R.string.txt_err)?.format(errorInfo.code, errorInfo.message)
            }
        }
    }

    open fun onCreateLoadingViewHolder(parent: ViewGroup): ViewHolder {
        return object : ViewHolder(context, parent, R.layout.item_content) {
            override fun onBindView(items: List<*>, position: Int) {
                itemView.itemContentInfo.text = context?.getText(R.string.txt_loading)
            }
        }
    }

    open fun onCreateEmptyViewHolder(parent: ViewGroup): ViewHolder {
        return object : ViewHolder(context, parent, R.layout.item_content) {
            override fun onBindView(items: List<*>, position: Int) {
                itemView.itemContentInfo.text = context?.getText(R.string.txt_empty)
            }
        }
    }

    open fun filterItemViewType(position: Int): Int = VIEW_TYPE_FILTER
    open fun abnormalItemCount(): Int = 1
    open fun isExpansionViewType(viewType: Int): Boolean = false

    open fun getCustomViewType(position: Int): Int {
        return super.getItemViewType(position)
    }

    open fun getCustomItemCount(): Int = items.size

    open fun isEmpty() = getCustomItemCount() == 0

    /**
     * adapter的下标与items的下标互转
     * @param position 待转换的下标
     * @param reverse true-items的下标->adapter下标, false-adapter下标->items下标[默认]
     */
    open fun mappingPosition(position: Int, reverse: Boolean = false): Int = position

    open fun onErrorWhenNotEmptyItems(errorInfo: ErrorInfo) {
        logger.e("onErrorWhenNotEmptyItems(${errorInfo.code})：${errorInfo.message}")
    }

    fun startLoading(forceLoading: Boolean = false) {
        if (forceLoading || items.isEmpty()) {
            isLoading = true
            isErrorMode = false
            notifyDataSetChanged()
        }
    }

    fun stopLoading() {
        isLoading = false
        notifyDataSetChanged()
    }


    fun showError(errorInfo: ErrorInfo, forceError: Boolean = false) {
        isLoading = false
        if (forceError || isEmpty()) {
            isErrorMode = true
            this.errorInfo = errorInfo
            notifyDataSetChanged()
        } else {
            onErrorWhenNotEmptyItems(errorInfo)
        }
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        if (debug) {
            logger.d("onAttachedToRecyclerView: $recyclerView")
        }
        WrapperUtil.onAttachedToRecyclerView(recyclerView) { layoutManager, oldLookup, position ->
            val spanCount = getCustomSpanCount(layoutManager, oldLookup, position)
            if (debug) {
                logger.d("getCustomSpanCount=$spanCount, position = $position, loading=$isLoading, empty = ${isEmpty()}, error = $isErrorMode")
            }
            if (spanCount >= 0) {
                spanCount
            } else {
                oldLookup?.getSpanSize(position) ?: 1
            }
        }
    }

    override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
        if (debug) {
            logger.d("onViewAttachedToWindow: $holder")
        }
        if (isRequiredHolderFullSpan(holder)) {
            WrapperUtil.setFullSpan(holder)
        }
    }

    protected open fun isRequiredHolderFullSpan(holder: RecyclerView.ViewHolder): Boolean {
        return when {
            isLoading -> true
            isErrorMode -> true
            isEmpty() -> true
            else -> false
        }.apply {
            if (debug) {
                logger.d("isRequiredHolderFullSpan: $this")
            }
        }
    }

    protected open fun getCustomSpanCount(
        layoutManager: GridLayoutManager,
        oldLookup: GridLayoutManager.SpanSizeLookup?,
        position: Int
    ): Int {
        return when {
            isLoading -> layoutManager.spanCount
            isErrorMode -> layoutManager.spanCount
            isEmpty() -> layoutManager.spanCount
            else -> -1
        }
    }

    abstract class ViewHolder(
        context: Context?,
        parent: ViewGroup,
        layoutResId: Int
    ) : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(layoutResId, parent, false)) {
        protected val logger =
            LoggerFactory.getLogger(this.javaClass, "${this.javaClass.simpleName} >>> ")
        protected val context by WeakRef { context }
        abstract fun onBindView(items: List<*>, position: Int)
    }

    abstract class ContentViewHolder<T>(
        context: Context?,
        parent: ViewGroup,
        layoutResId: Int
    ) : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(layoutResId, parent, false)) {
        protected val logger =
            LoggerFactory.getLogger(this.javaClass, "${this.javaClass.simpleName} >>> ")
        protected val context by WeakRef { context }
        abstract fun onBindView(items: List<T>, item: T, position: Int)
        open fun onBindView(items: List<T>, item: T, position: Int, payloads: MutableList<Any>) =
            Unit
    }

    class UnknownViewHolderException : Exception("未知的ViewHolder类型")

    companion object {
        const val VIEW_TYPE_FILTER = Integer.MAX_VALUE
        private const val VIEW_TYPE_EMPTY = Integer.MAX_VALUE - 1
        private const val VIEW_TYPE_ERROR = Integer.MAX_VALUE - 2
        private const val VIEW_TYPE_LOADING = Integer.MAX_VALUE - 3
    }

    private var onItemClickListener: OnItemClickListener<T>? = null
    fun setOnItemClickListener(listener: OnItemClickListener<T>) {
        this.onItemClickListener = listener
    }


    interface OnItemClickListener<T> {
        fun onItemClick(dataList: List<T>, data: T, position: Int)
    }
}